/*
 * TabbedPane.java  0.4.0 / Oct 27, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
package com.cute4j.swing;

import java.util.ArrayList;

import com.cute4j.swing.layouts.LayoutManager;
import com.cute4j.swing.properties.TabbedPaneProperties;

/**
 * TabbedPane Component.
 *
 * @cute4j.native
 *    generate="header"
 *
 * @author Slobodan Vrkacevic
 * @version 0.4.0
 */
public class TabbedPane extends Container
                        implements SwingConstants, TabbedPaneProperties {

// Static Initialization. ------------------------------------------------------

  static {
    _initialize();
  }

// Package-Private Fields. -----------------------------------------------------

  private ArrayList pages;
  private int tabPlacement;
  private int selectedIndex;

// Public Constructors. --------------------------------------------------------

  /**
   * Constructs new TabbedPane object.
   */
  public TabbedPane() {
    super(null);
    pages = new ArrayList(3);
    tabPlacement = TOP;
    selectedIndex = -1;
  }

  /**
   * Constructs new TabbedPane object.
   *
   * @param tabPlacement the tab placement
   */
  public TabbedPane(int tabPlacement) {
    this();
    if ((tabPlacement < TOP) || (tabPlacement > RIGHT)) {
      throw new IllegalArgumentException("Invalid tab placement");
    }
    this.tabPlacement = tabPlacement;
    _setTabPlacement(_delegate, tabPlacement); // NOPMD
  }

// Public Methods. -------------------------------------------------------------

  /**
   * Adds new tab.
   *
   * @param title the tab title
   * @param component the tab component
   *
   * @return the component argument
   */
  public Component addTab(String title, Component component) {
    return insertTab(title, null, component, null, -1);
  }

  /**
   * Adds new tab.
   *
   * @param title the tab title
   * @param icon the tab icon
   * @param component the tab component
   *
   * @return the component argument
   */
  public Component addTab(String title, Icon icon, Component component) {
    return insertTab(title, icon, component, null, -1);
  }

  /**
   * Adds new tab.
   *
   * @param title the tab title
   * @param icon the tab icon
   * @param component the tab component
   * @param tooltip the tab tooltip
   *
   * @return the component argument
   */
  public Component addTab(String title, Icon icon, Component component,
                            String tooltip) {
    return insertTab(title, icon, component, tooltip, -1);
  }

  /**
   * Inserts new tab at specified position.
   *
   * @param title the tab title
   * @param icon the tab icon
   * @param component the tab component
   * @param tooltip the tab tooltip
   * @param index the index
   *
   * @return the component argument
   */
  public Component insertTab(String title, Icon icon, // NOPMD
                             Component component, String tooltip, // NOPMD
                             int index) { // NOPMD
    if (component != null) {
      if (title == null) {
        title = component.getName(); // NOPMD
      }
      addImpl(component, title, index);
      Page page = new Page(title, tooltip);
      if ((index < 0) || (index >= pages.size())) {
        index = pages.size();
        pages.add(page);
      } else {
        pages.add(index, page);
      }
      if (tooltip == null) {
        tooltip = component.getToolTipText(); // NOPMD
        page.toolTipText = tooltip;
      }
      if (tooltip != null) {
        _setTabToolTipText(_delegate, index, tooltip);
      }
      String whatsThisText = component.getWhatsThisText();
      if (whatsThisText != null) {
        page.whatsThisText = whatsThisText;
        _setTabWhatsThisText(_delegate, index, whatsThisText);
      }
      if (icon != null) {
        SwingIconSet iconSet = new SwingIconSet(this);
        page.iconSet = iconSet;
        _setTabIcon(_delegate, index, iconSet);
        iconSet.setIcon(icon, null);
      }
    }
    return component;
  }

  /**
   * Returns tab placement.
   *
   * @return tab placement
   */
  public int getTabPlacement() {
    return tabPlacement;
  }

  /**
   * Sets tab placement.
   *
   * @param tabPlacement the tab placement
   */
  public void setTabPlacement(int tabPlacement) {
    if ((tabPlacement < TOP) || (tabPlacement > RIGHT)) {
      throw new IllegalArgumentException("Invalid tab placement");
    }
    final int oldTabPlacement = this.tabPlacement;
    if (oldTabPlacement != tabPlacement) {
      disposedCheck();
      this.tabPlacement = tabPlacement;
      _setTabPlacement(_delegate, tabPlacement);
      invalidate();
      firePropertyChange(PROPERTY_TAB_PLACEMENT, oldTabPlacement, tabPlacement);
    }
  }

  /**
   * Returns index of the selected component.
   *
   * @return the selected component
   */
  public int getSelectedIndex() {
    return selectedIndex;
  }

  /**
   * Sets index of the selected component.
   *
   * @param selectedIndex the selected index
   */
  public void setSelectedIndex(int selectedIndex) {
    if (this.selectedIndex != selectedIndex) {
      disposedCheck();
      _setCurrentIndex(_delegate, selectedIndex);
    }
  }

  /**
   * Returns currently selected component.
   *
   * @return currently selected component
   */
  public Component getSelectedComponent() {
    if (selectedIndex >= 0) {
      return getComponent(selectedIndex);
    }
    return null;
  }

  /**
   * Sets currently selected component.
   *
   * @param component the component to be selected
   */
  public void setSelectedComponent(Component component) {
    int index = getComponentIndex(component);
    if (index == -1) {
      throw new IllegalArgumentException("Invalid component");
    }
    setSelectedIndex(index);
  }

  /**
   * Returns <tt>true</tt> if the tab at specified index is enabled.
   *
   * @param index the component index
   *
   * @return <tt>true</tt> if the tab at specified index is enabled
   */
  public boolean isEnabledAt(int index) {
    return getPage(index).enabled;
  }

  /**
   * Enables/disables the component at specified index.
   *
   * @param index the component index
   * @param enabled <tt>true</tt> if enabled
   */
  public void setEnabledAt(int index, boolean enabled) {
    Page page = getPage(index);
    if (page.enabled != enabled) {
      disposedCheck();
      page.enabled = enabled;
      _setTabEnabled(_delegate, index, enabled);
    }
  }

  /**
   * Returns the title of the tab at specified index.
   *
   * @param index the component index
   *
   * @return the title of the tab at specified index
   */
  public String getTitleAt(int index) {
    return getPage(index).titleText;
  }

  /**
   * Sets title of the tab at specified index.
   *
   * @param index the tab index
   * @param title the title
   */
  public void setTitleAt(int index, String title) {
    Page page = getPage(index);
    final String oldTitle = page.titleText;
    if (oldTitle == null) {
      if (title == null) {
        return;
      }
    } else {
      if (oldTitle.equals(title)) {
        return;
      }
    }
    disposedCheck();
    _setTabTitleText(_delegate, index, title);
    page.titleText = title;
    firePropertyChange(PROPERTY_INDEX_FOR_TITLE, -1, index);
  }

  /**
   * Returns the tool tip text of the tab at specified index.
   *
   * @param index the tab index
   *
   * @return the tool tip text  of the tab at specified index
   */
  public String getToolTipTextAt(int index) {
    return getPage(index).toolTipText;
  }

  /**
   * Sets tool tip text of the tab at specified index.
   *
   * @param index the tab index
   * @param toolTipText the tool tip text
   */
  public void setToolTipTextAt(int index, String toolTipText) {
    Page page = getPage(index);
    final String oldToolTipText = page.titleText;
    if (oldToolTipText == null) {
      if (toolTipText == null) {
        return;
      }
    } else {
      if (oldToolTipText.equals(toolTipText)) {
        return;
      }
    }
    disposedCheck();
    _setTabToolTipText(_delegate, index, toolTipText);
    page.toolTipText = toolTipText;
  }

  /**
   * Returns the what's this text of the tab at specified index.
   *
   * @param index the tab index
   *
   * @return the what's this text of the tab at specified index
   */
  public String getWhatsThisText(int index) {
    return getPage(index).whatsThisText;
  }

  /**
   * Sets what's this text of the tab at specified index.
   *
   * @param index the tab index
   * @param whatsThisText the what's this text
   */
  public void setWhatsThisText(int index, String whatsThisText) {
    Page page = getPage(index);
    final String oldWhatsThisText = page.whatsThisText;
    if (oldWhatsThisText == null) {
      if (whatsThisText == null) {
        return;
      }
    } else {
      if (oldWhatsThisText.equals(whatsThisText)) {
        return;
      }
    }
    disposedCheck();
    _setTabWhatsThisText(_delegate, index, toolTipText);
    page.whatsThisText = whatsThisText;
  }

  /**
   * Returns icon of the specified tab.
   *
   * @param index the tab index
   *
   * @return icon of the specified tab
   */
  public Icon getIconAt(int index) {
    SwingIconSet iconSet = getPage(index).iconSet;
    if (iconSet != null) {
      return iconSet.getIcon();
    }
    return null;
  }

  /**
   * Sets icon of the specified tab.
   *
   * @param index the tab index
   * @param icon the icon
   */
  public void setIconAt(int index, Icon icon) {
    Page page = getPage(index);
    SwingIconSet iconSet = page.iconSet;
    disposedCheck();
    if (icon == null) {
      if (iconSet != null) {
        iconSet.setIcon(null, null);
        if (iconSet.isEmpty()) {
          _setTabIcon(_delegate, index, null);
          page.iconSet = null;
        }
      }
    } else {
      if (iconSet == null) {
        iconSet = new SwingIconSet(this);
        page.iconSet = iconSet;
        _setTabIcon(_delegate, index, iconSet);
      }
      iconSet.setIcon(icon, null);
    }
  }

  /**
   * Returns disabled icon of the specified tab.
   *
   * @param index the tab index
   *
   * @return disabled icon of the specified tab
   */
  public Icon getDisabledIconAt(int index) {
    SwingIconSet iconSet = getPage(index).iconSet;
    if (iconSet != null) {
      return iconSet.getDisabledIcon();
    }
    return null;
  }

  /**
   * Sets disabled icon of the specified tab.
   *
   * @param index the tab index
   * @param icon the Disabled icon
   */
  public void setDisabledAt(int index, Icon icon) {
    Page page = getPage(index);
    SwingIconSet iconSet = page.iconSet;
    disposedCheck();
    if (icon == null) {
      if (iconSet != null) {
        iconSet.setDisabledIcon(null, null);
        if (iconSet.isEmpty()) {
          _setTabIcon(_delegate, index, null);
          page.iconSet = null;
        }
      }
    } else {
      if (iconSet == null) {
        iconSet = new SwingIconSet(this);
        page.iconSet = iconSet;
        _setTabIcon(_delegate, index, iconSet);
      }
      iconSet.setDisabledIcon(icon, null);
    }
  }

// Overridden Methods. ---------------------------------------------------------

  /**
   * {@inheritDoc}
   */
  public void setLayout(LayoutManager layoutManager) {
    // empty method
  }

  /**
   * {@inheritDoc}
   */
  public void invalidate() {
    if (valid) {
      valid = false;
      if (visible) {
        disposedCheck();
        _validate(_delegate);
      }
    }
  }

  /**
   * {@inheritDoc}
   */
  public void validate() {
    if (!valid) {
      disposedCheck();
      valid = true;
      final Component[] components = this.components;
      for (int i = componentCount; --i >= 0;) {
        components[i].validate();
      }
    }
  }

  /**
   * {@inheritDoc}
   */
  public Component add(Component component) {
    if (component != null) {
      return insertTab(component.getName(), null, component, null, -1);
    }
    return null;
  }

  /**
   * {@inheritDoc}
   */
  public Component add(Component component, int index) {
    if (component != null) {
      return insertTab(component.getName(), null, component, null, index);
    }
    return null;
  }

  /**
   * {@inheritDoc}
   */
  public Component add(Component component, Object constraints) {
    if (constraints instanceof String) {
      return insertTab((String)constraints, null, component, null, -1);
    } else if (constraints instanceof Icon) {
      return insertTab(null, (Icon)constraints, component, null, -1);
    } else if (component != null) {
      return insertTab(component.getName(), null, component, null, -1);
    }
    return null;
  }

  /**
   * {@inheritDoc}
   */
  public Component add(Component component, Object constraints, int index) {
    Icon icon = null;
    if (constraints instanceof Icon) {
      icon = (Icon)constraints;
    }
    String title = null;
    if (constraints instanceof String) {
      title = (String)constraints;
    }
    return insertTab(title, icon, component, null, index);
  }

  /**
   * {@inheritDoc}
   */
  protected String paramString() {
    StringBuffer sb = new StringBuffer(80);
    sb.append(super.paramString());
    sb.append(",tabPlacement=");
    switch (tabPlacement) { // NOPMD
      case TOP:
        sb.append("TOP");
        break;
      case LEFT:
        sb.append("LEFT");
        break;
      case BOTTOM:
        sb.append("BOTTOM");
        break;
      case RIGHT:
        sb.append("RIGHT");
        break;
    }
    return super.paramString();
  }

  void insertChild(Component child, Object constraints, int index) {
    if (constraints == null) {
      _insertTab(_delegate, child._delegate, index, null);
    } else {
      _insertTab(_delegate, child._delegate, index, constraints.toString());
    }
  }

// Package-Private Methods. ----------------------------------------------------

  Page getPage(int index) {
    if ((index < 0) || (index >= pages.size())) {
      throw new IllegalArgumentException("Invalid component index");
    }
    return (Page)pages.get(index);
  }

// Native Callbacks. -----------------------------------------------------------

  void _currentChanged(int selectedIndex) {
    final int oldSelectedIndex = this.selectedIndex;
    if (oldSelectedIndex != selectedIndex) {
      this.selectedIndex = selectedIndex;
      firePropertyChange(PROPERTY_SELECTED_INDEX, oldSelectedIndex,
                         selectedIndex);
    }
  }

// Package-Private Native Methods. ---------------------------------------------

  native long _create();

  native void _insertTab(long delegate, long tabDelegate, int index, String title);

  native void _removeChild(long delegate, long childDelegate);

  native void _setTabPlacement(long delegate, int tabPlacement);

  native void _setTabEnabled(long delegate, int index, boolean enabled);

  native void _setTabTitleText(long delegate, int index, String titleText);

  native void _setTabToolTipText(long delegate, int index, String toolTipText);

  native void _setTabWhatsThisText(long delegate, int index,
                                   String whatsThisText);

  native void _setTabIcon(long delegate, int index, SwingIconSet iconSet);

  native void _setCurrentIndex(long delegate, int index);

// Private Static Native Methods. ----------------------------------------------

  private static native void _initialize();

// Inner Classes. --------------------------------------------------------------

  /**
   * Tab page class.
   */
  static final class Page {
    boolean enabled;
    String titleText;
    String toolTipText;
    String whatsThisText;
    SwingIconSet iconSet;
    Page(String titleText, String toolTipText) {
      enabled = true;
      this.titleText = titleText;
      this.toolTipText = toolTipText;
    }
  }

}
